import {getOppositeDirection} from '@/utils'
/* 爬虫行为接口，用来定义和拓展爬虫的行为的原型扩展 */
export default class CreepExtension extends Creep {
    /*
    **  由于使用普通的move和moveTo方法在遇到比较狭窄的路况时，爬虫之间很难“达成一致”，从而导致tick的浪费和无用路径的增加 
    **  所以底下将进行一系列的爬虫对穿逻辑的实现，使爬虫能协商一致
    */
    /* 新的move函数 */
    public move_(direction: DirectionConstant): CreepMoveReturnCode | ERR_INVALID_TARGET
    {
        const moveResult = this.move(direction)
        if (moveResult != OK) return moveResult
        // 如果ok的话，有可能撞上东西了或者一切正常
        const currentPos = `${this.pos.x}/${this.pos.y}`
        if (this.memory.prePos && currentPos == this.memory.prePos)
        {
            // 这个时候确定在原点驻留了
            // 尝试进行对穿
            const crossResult = this.memory.disableCross ? ERR_BUSY : this.matualCross(direction)
            if (crossResult != OK)
            {
                delete this.memory._move
                return ERR_INVALID_TARGET
            }
        }
        this.memory.prePos = currentPos
        return OK
    }
    /* 向指定方向发起对穿 */
    public matualCross(direction: DirectionConstant):OK | ERR_BUSY | ERR_NOT_FOUND
    {
        // 获取前方位置上的creep
        const fontPos = this.pos.directionToPos(direction)
        // 在出口、边界
        if (!fontPos) return ERR_NOT_FOUND
        const fontCreep = fontPos.lookFor(LOOK_CREEPS)[0] || fontPos.lookFor(LOOK_POWER_CREEPS)[0]
        if (!fontCreep) return ERR_NOT_FOUND
        this.say("👉")
        if (fontCreep.requireCross(getOppositeDirection(direction))) this.move(direction)
        return OK
    }

    /* 请求对穿函数 如果返回为true则可以直接移动 */
    public requireCross(direction: DirectionConstant):boolean {
        // 没有记忆说明已经凉了，直接移动
        if (!this.memory) return true
        // 拒绝对穿
        if (this.memory.standed){
            this.say('👊')
            return false
        }
        // 同意对穿
        this.say('👌')
        this.move(direction)
        // 被对穿的爬必须删除原有寻路记忆，这样才能正确寻路
        // if (!this.memory.roomMove) this.memory.roomMove = {}
        // if (this.memory.roomMove.path) delete this.memory.roomMove.path
        return true
    }
    
    /**
     * 无视 Creep 的便捷寻路
     * 
     * @param target 要移动到的位置
     */
    public runTo(target: RoomPosition): CreepMoveReturnCode | ERR_NO_PATH | ERR_INVALID_TARGET | ERR_NOT_FOUND {
        // const baseCost = Game.cpu.getUsed()
        const moveResult = this.moveTo(target, {
            reusePath: 20,
            ignoreCreeps: true,
            costCallback: (roomName, costMatrix) => {
                if (roomName == this.room.name){
                    const restrictedPos = this.room.getStandedPos()
                    if (restrictedPos)
                    {
                        for (var i of restrictedPos)
                        {
                            // 如果不是自己所在的位置
                            if (i != this.pos)
                            {
                            costMatrix.set(i.x,i.y,0xff)
                            }
                        }
                    }
                }
                return costMatrix
            }
        })

        return moveResult
    }
    
    /* 带有对穿功能的无视creep的便捷寻路 (如果对方也有对穿功能，那就可以发起对穿)*/
    public rush(pos_: RoomPosition):void
    {
        var path_ = this.pos.findPathTo(pos_,{
            
            ignoreCreeps:true,
            maxOps:400,
            costCallback: (roomName,costMatrix) => {
                if (roomName == this.room.name){
                    const restrictedPos = this.room.getStandedPos()
                    if (restrictedPos)
                    {
                        for (var i of restrictedPos)
                        {
                             // 如果不是自己所在的位置
                            if (i != this.pos)
                            {
                            costMatrix.set(i.x,i.y,0xff)
                            }
                        }
                    }
                }
                return costMatrix
            }
        })
        if (path_.length > 0)
        {
            this.move_(path_[0].direction)
        }
        else
        {
             // 如果实在没有路，就用最朴素的方式 moveTo
            this.moveTo(pos_)
        }
    }
    /* 房间内寻路 未使用 */
    public findRoomPath(target:RoomPosition,stand?:boolean):string | null{
        if (!global.roomCache) global.roomCache = {}
        if (!this.memory.roomMove) this.memory.roomMove = {}
        this.memory.roomMove.index = 0
        const routeKey = `${this.room.serializePos(this.pos)} ${this.room.serializePos(target)}`
        var route = global.roomCache[routeKey]
        if (route) {return}
        // 没有相关缓存就创建缓存
        const result = this.pos.findPathTo(target,{
            ignoreCreeps:true,
            plainCost:1,
            swampCost:10,
            costCallback:(roomName,costMatrix) => {
                if (roomName == this.room.name){
                    if(stand && stand == true)
                    {
                        const restrictedPos = this.room.getStandedPos()
                        if (restrictedPos)
                        {
                            for (var i of restrictedPos)
                            {
                             // 如果不是自己所在的位置
                                if (i != this.pos)
                                {
                                costMatrix.set(i.x,i.y,0xff)
                                }
                            }
                        }
                    }
                }
                return costMatrix
            }
        })
        // 如果没找到就返回null
        if (result.length <= 0) return null
        // 将结果压缩成字符串并输出
        var serialzePath = result.map((obj,index)=>{
            return obj.direction
        }).join('')
        return serialzePath
    }

    /* 使用RoomPath缓存进行移动 未使用 */
    public goByRoomCache():CreepMoveReturnCode | ERR_NO_PATH | ERR_NOT_IN_RANGE | ERR_INVALID_TARGET{
        if (!this.memory.roomMove) return ERR_NO_PATH
        if (!this.memory.roomMove.prePos) this.memory.roomMove.prePos =  'start'
        if (!this.memory.roomMove.stayTick) this.memory.roomMove.stayTick = 0
        const index = this.memory.roomMove.index
        // 移动索引超过数组上限代表到达目的地
        if (!this.memory.roomMove.path) this.memory.roomMove.path = ''
        if (index >= this.memory.roomMove.path.length){
            delete this.memory.roomMove.path
            return OK
        }
        const direction = <DirectionConstant>Number(this.memory.roomMove.path[index])
        const goResult = this.move_(direction)
        if (this.memory.roomMove.prePos == `${this.pos.x}/${this.pos.y}`)
        {
            this.memory.roomMove.stayTick += 1
        }
        else this.memory.roomMove.stayTick = 0
        this.memory.roomMove.prePos = `${this.pos.x}/${this.pos.y}`
        // 移动成功，更新下次移动索引
        if (goResult == OK) this.memory.roomMove.index ++
        return goResult
    }
    
    /* 远程房间寻路 */
    public findFarPath(target:RoomPosition,range:number):string|null{
        if (!global.routeCache) global.routeCache = {}
        if (!this.memory.farMove) this.memory.farMove = { }
        this.memory.farMove.index = 0
        const routeKey = `${this.room.serializePos(this.pos)} ${this.room.serializePos(target)}`
        var route = global.routeCache[routeKey]
        // 有route则直接返回
        if (route)
        {
            // console.log("已有路径")
            return route
        }
        
        const result = PathFinder.search(this.pos,{pos:target,range:range},{
            plainCost:2,
            swampCost:10,
            maxOps:8000,
            roomCallback:roomName=>{
                // 在绕过房间列表的房间将直接不让走
                if (Memory.bypassRooms && Memory.bypassRooms.includes(roomName)) return false
                const room_ = Game.rooms[roomName]
                // 如果房间未开放，则略去
                if (roomName === 'E40S50') console.log('error room')
                if (!room_) return
                // console.log(1)
                let costs = new PathFinder.CostMatrix
                // 将其他地图中的道路设置为1，无法行走的建筑设置为255
                room_.find(FIND_STRUCTURES).forEach(struct=>{
                    if (struct.structureType === STRUCTURE_ROAD)
                    {
                        costs.set(struct.pos.x,struct.pos.y,1)
                    }
                    else if (struct.structureType !== STRUCTURE_CONTAINER && 
                        (struct.structureType !==STRUCTURE_RAMPART || !struct.my))
                        costs.set(struct.pos.x,struct.pos.y,0xff)
                })
                /* 防止撞到其他虫子造成堵虫 */
                room_.find(FIND_HOSTILE_CREEPS).forEach(creep=>{
                    costs.set(creep.pos.x,creep.pos.y,255)
                })
                // 将禁止通行点也设置为255
                const restrictedPos = room_.getStandedPos()
                for (const rep of restrictedPos)
                {
                        costs.set(rep.x,rep.y,0xff)
                }
                return costs
                }
            })
            // 没找到就返回 null
        if (result.path.length <= 0) return null
        console.log(`房间${target},ops:${result.ops},cost:${result.cost},[0]:${result.path[0]}`)
        console.log(this.serializeFarPath(result.path))
        route = this.serializeFarPath(result.path)
        // 如果寻路成功
        if (!result.incomplete) global.routeCache[routeKey] = route
        return route
    }

    /* 压缩路径 */
    public serializeFarPath(positions:RoomPosition[]):string{
        if (positions.length == 0) return ''
        // 确保路径里第一个位置是自己当前的位置
        if (!positions[0].isEqualTo(this.pos)) positions.splice(0,0,this.pos)

        return positions.map((pos, index) => {
            // 最后一个位置就不用再移动
            if (index >= positions.length - 1) return null
            // 由于房间边缘地块会有重叠，所以这里筛除掉重叠的步骤
            if (pos.roomName != positions[index + 1].roomName) return null
            // 获取到下个位置的方向
            return pos.getDirectionTo(positions[index + 1])
        }).join('')
    }
    // 使用缓存进行移动
    public goByCache(): CreepMoveReturnCode | ERR_NO_PATH | ERR_NOT_IN_RANGE | ERR_INVALID_TARGET {
        if (!this.memory.farMove) return ERR_NO_PATH

        const index = this.memory.farMove.index
        // 移动索引超过数组上限代表到达目的地
        if (index >= this.memory.farMove.path.length) {
            delete this.memory.farMove.path
            return OK
        }
        // 获取方向，进行移动
        const direction = <DirectionConstant>Number(this.memory.farMove.path[index])
        const goResult = this.move_(direction)
        
        // 移动成功，更新下次移动索引
        if (goResult == OK) this.memory.farMove.index ++
        return goResult
    }

    // 远程移动
    public farMoveTo(target:RoomPosition,range:number):CreepMoveReturnCode | ERR_NO_PATH | ERR_NOT_IN_RANGE | ERR_INVALID_TARGET {
        if (this.memory.farMove == undefined) this.memory.farMove = { }
        // 确认目标没有变化，如果变化了就重新规划路线
        const targetPosTag = this.room.serializePos(target)
        if (targetPosTag !== this.memory.farMove.targetPos)
        {
            this.memory.farMove.targetPos = targetPosTag
            this.memory.farMove.path = this.findFarPath(target,range)
        }
        // 确认缓存有没有被清除
        if (!this.memory.farMove.path)
        {
            this.memory.farMove.path = this.findFarPath(target,range)
        }
        // 还为空的话就是没有找到路径
        if (!this.memory.farMove.path)
        {
            delete this.memory.farMove.path
            return OK
        }
        // 使用缓存进行移动
        const goResult = this.goByCache()
        // 如果发生撞停或者参数异常，说明缓存可能存在问题，移除缓存
        // console.log(goResult)
        if (goResult === ERR_INVALID_TARGET){
            delete this.memory.farMove
        }
        else if (goResult != OK && goResult != ERR_TIRED)
        {
            this.say(`远程寻路${goResult}`)
        }
        return goResult
    }

    // 远程移动封装
    public remote(roomName:string):void
    {
        if (this.room.name != roomName)
        {
            this.farMoveTo(new RoomPosition(25,25,roomName),20)
        }
    }
    



}